/**
 * @author kathy
 *
 */
public class NarrowStructuresTest {
	private NarrowStructure deepAndNarrow;
	private BroadStructure shallowAndBroad;
	private int cycles;
	
	private static final long DEEP_STRUCTURE_SIZE_DEFAULT = 100;
	private static final long DEEP_PERCENTAGE_DEFAULT = 50;
	private static final int SHALLOW_BREADTH_DEFAULT = 10;
	private static final int CYCLES_DEFAULT = 2;
	
	NarrowStructuresTest(long deepElements, long deepPercentage, int shalowBreadth, int cycles) {
		long totalElements = (long)(deepElements*100.0/deepPercentage);
		long shallowElements = totalElements - deepElements;
		System.out.println("Creating deep and narrow structure of "+deepElements+" elements");
		this.deepAndNarrow = new NarrowStructure(deepElements);
		System.out.println("Creating shallow and broad structure of "+shallowElements+" elements");
		this.shallowAndBroad = new BroadStructure(shallowElements, shalowBreadth);	
		System.out.println("Will invoke "+cycles+" GC cycles");
		this.cycles = cycles;	
	}
	
	private void execute() {
		System.out.println("Traversing structures before GC");
		long deepAndNarrowFound = deepAndNarrow.traverse();
		long shallowAndBroadFound = shallowAndBroad.traverseDFS();
			
		System.out.println("Traversal of deep and narrow structure of " + deepAndNarrow.getSize() +
				" elements, found "+ deepAndNarrowFound +" nodes");
		System.out.println("Traversal of shallow and broad structure by DFS of " + shallowAndBroad.getSize() +
				" elements, found "+ shallowAndBroadFound +" nodes");
		int times = 0;
		while(this.cycles != times) {
			times++;
			System.out.println("Starting cycle number "+times);
			System.out.println("Calling GC");
			System.gc();
			System.out.println("Done GC");
			
			int replaceRate = 200;
			System.out.println("Traversing narrow tructure after GC, replacing every object number "+replaceRate);
			deepAndNarrowFound = deepAndNarrow.traverseWithReplace(replaceRate);
			System.out.println("Traversal of deep and narrow structure of " + deepAndNarrow.getSize() +
					" elements, found "+ deepAndNarrowFound +" nodes");
			System.out.flush();
			System.out.println("Traversing broad tructure after GC");
			shallowAndBroadFound = shallowAndBroad.traverseDFS();
			System.out.println("Traversal of shallow and broad structure by DFS of " + shallowAndBroad.getSize() +
					" elements, found "+ shallowAndBroadFound +" nodes");
			System.out.flush();
		}
	}

	private static void help(boolean full) {
		if(full) {
			System.out.println("  NarrowStructuresTest - allocates specified amount of objects ");
			System.out.println("  so that part of the objects is in a linked list and another part is");
			System.out.println("  in a structure with a specified amount of references per member.");
			System.out.println("  Upon creation, ...\n");
		}
		System.out.println("USAGE: NarrowStructuresTest [--help] [narrow] [percentage] [breadth] [cycles]");
		System.out.println("\t --help      prints this message");
		System.out.println("\t narrow      amount of objects in a long linked list("+ DEEP_STRUCTURE_SIZE_DEFAULT +")");
		System.out.println("\t percentage  percentage of objects in a long linked list("+ DEEP_PERCENTAGE_DEFAULT +")");
		System.out.println("\t breadth     number of pointers in other objects("+ SHALLOW_BREADTH_DEFAULT +")");
		System.out.println("\t cycles      number of GC cycles("+ CYCLES_DEFAULT +")");
		System.exit(1);
	}
		
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		long narrowElements = DEEP_STRUCTURE_SIZE_DEFAULT;
		long narrowPercentage = DEEP_PERCENTAGE_DEFAULT;
		int shalowBreadth = SHALLOW_BREADTH_DEFAULT;
		int cycles = CYCLES_DEFAULT;

		if(args.length == 1 && ("--help").equals(args[0])) {
			help(true);
		}
		
		try {
			if (args.length > 0) {
				narrowElements = Integer.parseInt(args[0]);
				if (narrowElements <= 0) {
					System.out.println("Bad usage: deep and narrow structure size should be at least 1\n");
					help(false);
				}
			}
	
			if (args.length > 1) {
				narrowPercentage = Integer.parseInt(args[1]);
				if (narrowPercentage <= 0 || narrowPercentage > 100) {
					System.out.println("Bad usage: percentage of elements in deep "
									+ "and narrow structure should be at least 1 and at most 100");
					help(false);
				}
			}
	
			if (args.length > 2) {
				shalowBreadth = Integer.parseInt(args[2]);
				if (shalowBreadth <= 0) {
					System.out.println("Bad usage: number of references in each  "
									+ "element of shallow and broad structure should be at least 1");
					help(false);
				}
			}
	
			if (args.length > 3) {
				cycles = Integer.parseInt(args[3]);
				if (cycles <= 0) {
					cycles = 0;
				}
			}
		} catch(Throwable t) {
			System.out.println("Bad usage.");
			help(true);
		}

		NarrowStructuresTest test = new NarrowStructuresTest(narrowElements, narrowPercentage, shalowBreadth, cycles);

		System.out.println("Starting the test");
		test.execute();
		System.out.println("Done test");
	}
}
